package utils

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"math/rand"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// GenerateMD5 生成MD5哈希值
func GenerateMD5(str string) string {
	hash := md5.New()
	hash.Write([]byte(str))
	return hex.EncodeToString(hash.Sum(nil))
}

// GenerateRandomString 生成随机字符串
func GenerateRandomString(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, length)
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := range b {
		b[i] = charset[r.Intn(len(charset))]
	}
	return string(b)
}

// ParseJSON 将JSON字符串解析为结构体
func ParseJSON(jsonStr string, v interface{}) error {
	return json.Unmarshal([]byte(jsonStr), v)
}

// ToJSON 将结构体转换为JSON字符串
func ToJSON(v interface{}) (string, error) {
	bytes, err := json.Marshal(v)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

// ValidateEmail 验证邮箱格式是否正确
func ValidateEmail(email string) bool {
	pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
	matched, _ := regexp.MatchString(pattern, email)
	return matched
}

// FormatTime 将时间格式化为指定格式的字符串
func FormatTime(t time.Time, layout string) string {
	if layout == "" {
		layout = "2006-01-02 15:04:05"
	}
	return t.Format(layout)
}

// ParseTime 将字符串解析为时间
func ParseTime(timeStr string, layout string) (time.Time, error) {
	if layout == "" {
		layout = "2006-01-02 15:04:05"
	}
	return time.Parse(layout, timeStr)
}

// GetIP 获取客户端IP地址
func GetIP(c *gin.Context) string {
	clientIP := c.ClientIP()
	// 处理代理情况
	xForwardedFor := c.Request.Header.Get("X-Forwarded-For")
	if xForwardedFor != "" {
		ips := strings.Split(xForwardedFor, ",")
		if len(ips) > 0 {
			clientIP = strings.TrimSpace(ips[0])
		}
	}
	return clientIP
}

// Pagination 计算分页参数
func Pagination(page, pageSize int) (offset, limit int) {
	if page <= 0 {
		page = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}
	if pageSize > 100 {
		pageSize = 100
	}
	offset = (page - 1) * pageSize
	limit = pageSize
	return
}

// ResponseError 统一错误响应
func ResponseError(c *gin.Context, code int, message string) {
	c.JSON(code, gin.H{
		"code":    code,
		"message": message,
		"data":    nil,
	})
}

// ResponseSuccess 统一成功响应
func ResponseSuccess(c *gin.Context, data interface{}, message string) {
	c.JSON(http.StatusOK, gin.H{
		"code":    http.StatusOK,
		"message": message,
		"data":    data,
	})
}

// StructToMap 将结构体转换为map
func StructToMap(obj interface{}) (map[string]interface{}, error) {
	data, err := json.Marshal(obj)
	if err != nil {
		return nil, err
	}

	var result map[string]interface{}
	err = json.Unmarshal(data, &result)
	if err != nil {
		return nil, err
	}

	return result, nil
}

// MapToStruct 将map转换为结构体
func MapToStruct(m map[string]interface{}, obj interface{}) error {
	data, err := json.Marshal(m)
	if err != nil {
		return err
	}

	return json.Unmarshal(data, obj)
}

// CopyStruct 复制结构体
func CopyStruct(src interface{}, dest interface{}) error {
	data, err := json.Marshal(src)
	if err != nil {
		return err
	}

	return json.Unmarshal(data, dest)
}

// SliceContains 判断切片是否包含指定元素
func SliceContains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

// RemoveDuplicates 移除切片中的重复元素
func RemoveDuplicates(slice []string) []string {
	seen := make(map[string]bool)
	result := []string{}
	for _, item := range slice {
		if !seen[item] {
			seen[item] = true
			result = append(result, item)
		}
	}
	return result
}

// SplitTags 将标签字符串分割为标签数组
func SplitTags(tagsStr string) []string {
	if tagsStr == "" {
		return []string{}
	}

	tags := strings.Split(tagsStr, ",")
	result := []string{}
	for _, tag := range tags {
		tag = strings.TrimSpace(tag)
		if tag != "" {
			result = append(result, tag)
		}
	}
	return result
}
func ExtractFileVideoFirstFrame(tempFilePath string) (string, error) {
	// 打开视频文件
	videoSrc, err := os.Open(tempFilePath)
	if err != nil {
		return "", fmt.Errorf("无法打开视频文件: %w", err)
	}
	defer videoSrc.Close()

	// 创建临时目录存储视频和提取的帧
	tempDir := os.TempDir()

	// 生成唯一的文件名
	timestamp := time.Now().UnixNano()
	videoExt := filepath.Ext(videoSrc.Name())
	videoTempPath := filepath.Join(tempDir, fmt.Sprintf("video_temp_%d%s", timestamp, videoExt))
	frameTempPath := filepath.Join(tempDir, fmt.Sprintf("frame_temp_%d.jpg", timestamp))

	// 将视频内容保存到临时文件
	videoTempFile, err := os.Create(videoTempPath)
	if err != nil {
		return "", fmt.Errorf("无法创建临时视频文件: %w", err)
	}
	defer func() {
		videoTempFile.Close()
		os.Remove(videoTempPath) // 清理临时文件
	}()

	// 将视频内容从multipart.File复制到临时文件
	_, err = videoTempFile.ReadFrom(videoSrc)
	if err != nil {
		return "", fmt.Errorf("无法保存视频内容到临时文件: %w", err)
	}

	// 使用FFmpeg提取第一帧
	cmd := exec.Command("ffmpeg", "-i", videoTempPath, "-ss", "00:00:00.001", "-vframes", "1", frameTempPath)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("FFmpeg提取视频帧失败: %w, 输出: %s", err, string(output))
	}

	return frameTempPath, nil
}

// ExtractVideoFirstFrame 从视频文件中提取第一帧作为封面图
// 返回提取的封面图文件路径
func ExtractVideoFirstFrame(videoFile *multipart.FileHeader) (string, error) {
	// 打开视频文件
	videoSrc, err := videoFile.Open()
	if err != nil {
		return "", fmt.Errorf("无法打开视频文件: %w", err)
	}
	defer videoSrc.Close()

	// 创建临时目录存储视频和提取的帧
	tempDir := os.TempDir()

	// 生成唯一的文件名
	timestamp := time.Now().UnixNano()
	videoExt := filepath.Ext(videoFile.Filename)
	videoTempPath := filepath.Join(tempDir, fmt.Sprintf("video_temp_%d%s", timestamp, videoExt))
	frameTempPath := filepath.Join(tempDir, fmt.Sprintf("frame_temp_%d.jpg", timestamp))

	// 将视频内容保存到临时文件
	videoTempFile, err := os.Create(videoTempPath)
	if err != nil {
		return "", fmt.Errorf("无法创建临时视频文件: %w", err)
	}
	defer func() {
		videoTempFile.Close()
		os.Remove(videoTempPath) // 清理临时文件
	}()

	// 将视频内容从multipart.File复制到临时文件
	_, err = videoTempFile.ReadFrom(videoSrc)
	if err != nil {
		return "", fmt.Errorf("无法保存视频内容到临时文件: %w", err)
	}

	// 使用FFmpeg提取第一帧
	cmd := exec.Command("ffmpeg", "-i", videoTempPath, "-ss", "00:00:00.001", "-vframes", "1", frameTempPath)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("FFmpeg提取视频帧失败: %w, 输出: %s", err, string(output))
	}

	return frameTempPath, nil
}

// ConvertM3U8ToMP4 将m3u8格式的视频转换为mp4格式
// 返回转换后的mp4文件路径和新的文件大小
func ConvertM3U8ToMP4(videoFile *multipart.FileHeader) (string, int64, error) {
	// 打开视频文件
	videoSrc, err := videoFile.Open()
	if err != nil {
		return "", 0, fmt.Errorf("无法打开视频文件: %w", err)
	}
	defer videoSrc.Close()

	// 创建临时目录存储视频和转换后的文件
	tempDir := os.TempDir()

	// 生成唯一的文件名
	timestamp := time.Now().UnixNano()
	m3u8TempPath := filepath.Join(tempDir, fmt.Sprintf("video_temp_%d.m3u8", timestamp))
	mp4TempPath := filepath.Join(tempDir, fmt.Sprintf("video_temp_%d.mp4", timestamp))

	// 将视频内容保存到临时文件
	m3u8TempFile, err := os.Create(m3u8TempPath)
	if err != nil {
		return "", 0, fmt.Errorf("无法创建临时m3u8文件: %w", err)
	}
	defer func() {
		m3u8TempFile.Close()
		os.Remove(m3u8TempPath) // 清理临时m3u8文件
	}()

	// 将视频内容从multipart.File复制到临时文件
	_, err = m3u8TempFile.ReadFrom(videoSrc)
	if err != nil {
		return "", 0, fmt.Errorf("无法保存视频内容到临时文件: %w", err)
	}

	// 使用FFmpeg将m3u8转换为mp4
	// 使用-c copy参数进行快速复制，不重新编码
	cmd := exec.Command("ffmpeg", "-i", m3u8TempPath, "-c", "copy", "-bsf:a", "aac_adtstoasc", mp4TempPath)
	output, err := cmd.CombinedOutput()
	if err != nil {
		// 转换失败时删除已创建的mp4文件
		if _, err := os.Stat(mp4TempPath); err == nil {
			os.Remove(mp4TempPath)
		}
		return "", 0, fmt.Errorf("FFmpeg转换m3u8到mp4失败: %w, 输出: %s", err, string(output))
	}

	// 获取转换后文件的大小
	fileInfo, err := os.Stat(mp4TempPath)
	if err != nil {
		os.Remove(mp4TempPath)
		return "", 0, fmt.Errorf("获取转换后文件信息失败: %w", err)
	}

	return mp4TempPath, fileInfo.Size(), nil
}
